-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local Singleton = require 'mc.singleton'
local crypt = require 'utils.crypt'
local kmc = require 'mc.kmc'
local log = require 'mc.logging'
local skynet = require 'skynet'
local bios_factory = require 'factory.bios_factory'
local obj_def = require 'macros.object_def'
local class = require 'mc.class'

-- kmc_client客户端，需要使用KMC加密的业务逻辑可以收口到这里
local kmc_client = class()

function kmc_client:update_pwd(data)
    if not data or data == '' then
        return ''
    end
    local old_data = self:decrypt_password(data)
    local new_data = self:encrypt_password(old_data)
    return new_data
end

function kmc_client:update_ciphertext()
    local prop_tbl = {
        obj_def.PROPERTY_BIOS_SETUP_PWD_NEW, obj_def.PROPERTY_BIOS_SETUP_PWD_OLD,
        obj_def.PROPERTY_BIOS_USER_SETUP_PWD_OLD, obj_def.PROPERTY_BIOS_USER_SETUP_PWD_NEW
    }
    local bios_ser = bios_factory.get_service('bios_service')
    for _, prop in pairs(prop_tbl) do
        local val = bios_ser:get_prop(prop)
        local new_val = self:update_pwd(val)
        bios_ser:set_prop(prop, new_val)
    end
end

function kmc_client:update_master_key_callback()
    local ok, err = pcall(function()
        self:update_ciphertext()
    end)

    if not ok then
        log:error('update bios password failed, err: %s.', tostring(err))
        return false
    end
    return true
end

function kmc_client:ctor(bus, app_name, dry_run)
    local ok, err = pcall(function()
        if dry_run == true then
            self.key_mgmt_client = {}
            self.m_domain_id = 0
            return
        end
        local key_mgmt_client = require 'key_mgmt.key_client_lib'
        local client = key_mgmt_client.new(bus, 'BIOS password', function(_, _)
            return self:update_master_key_callback()
        end, skynet.getenv('BIOS_KSF_FILE_NAME'), skynet.getenv('BIOS_KSF_BACK_NAME'))
        self.key_mgmt_client = client
        self.m_domain_id = client.m_domain_id
    end)
    if not ok then
        log:error('[bios]init key mgmt fail, err %s', err)
    end
    log:notice('[bios]init key mgmt success')
end

function kmc_client:get_domain_id()
    return self.m_domain_id
end

-- 用户密码解密
function kmc_client:decrypt_password(encrypt_string)
    local cipher_text = crypt.convert_string_to_ciphertext(encrypt_string)
    local ok, ret = pcall(kmc.decrypt_data, self.m_domain_id, cipher_text)
    if not ok then
        log:debug("[bios]decrypt failed, %s", tostring(ret))
        return ""
    end
    return ret
end

-- 用户密码加密
function kmc_client:encrypt_password(plaintext)
    local pwd_bytes = kmc.encrypt_data(self.m_domain_id, kmc.WsecAlgId.WSEC_ALGID_AES256_GCM,
        kmc.WsecAlgId.WSEC_ALGID_UNKNOWN, plaintext)
    return crypt.convert_ciphertext_to_string(pwd_bytes, #pwd_bytes)
end

return Singleton(kmc_client)
